﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using CS.Diagnostics;

namespace CS.Beehive
{
    /// <summary>
    ///  Tcp客户端
    /// </summary>
    public class TcpClient :TcpSetting, IDisposable
    {

        #region 内部变量

        private IPEndPoint _serverEndPoint;

        private Socket _clientSocket;

        //internal ReceiveBuffer ReceiveBuffer;

        private readonly SocketAsyncEventArgsPool _readerPool;

        private readonly SocketAsyncEventArgsPool _writerPool;

        #endregion

        #region 保护变量

        #endregion

        #region 构造，析构 , 初始化

        protected TcpClient()
        {
            //ReceiveBuffer = new ReceiveBuffer(ReceiveBufferSize);

            _readerPool = new SocketAsyncEventArgsPool(2);
            _writerPool = new SocketAsyncEventArgsPool(2);
        }

        #region Close IDisposable

        /// <summary>
        /// 关闭TcpChannel连接
        /// </summary>
        public void Close()
        {
            Dispose();
        }

        /// <summary>
        /// 关闭并断开SocketChannel连接
        /// <remarks>Release instance</remarks>
        /// </summary>
        public void Dispose()
        {
            //必须为true
            Dispose(true);
            //通知垃圾回收机制不再调用终结器（析构器）
            GC.SuppressFinalize(this);

        }

        /// <summary>
        /// 回收器的回收
        /// </summary>
        ~TcpClient()
        {
            //必须为false
            Dispose(false);
        }

        private bool _disposed;

        protected virtual void Dispose(bool disposing)
        {
            if (_disposed)
            {
                return;
            }
            if (disposing) // 清理托管资源
            {
                //ReceiveBuffer = null; //Note:实际上还是由回收机制管理

                //清理读写池
                var x = _writerPool.Pop();
                while (x != null)
                {
                    x.Dispose();
                    x = _writerPool.Pop();
                }

                x = _readerPool.Pop();
                while (x != null)
                {
                    x.Dispose();
                    x = _readerPool.Pop();
                }

            }

            // 清理非托管资源
            try
            {
                _clientSocket.Shutdown(SocketShutdown.Send);
            }
            catch (Exception ex)
            {
                // Throw if client has closed, so it is not necessary to catch.
                OnTcpException(ex);
            }
            finally
            {
                //客户端断开时，SocketChannel为null
                if (_clientSocket != null)
                {
                    _clientSocket.Close();
                    _clientSocket = null;
                }
            }

            //让类型知道自己已经被释放
            _disposed = true;
        }



        #endregion

        #endregion

        #region 事件 定义

        /// <summary>
        /// 异常事件
        /// </summary>
        public event EventHandler<TcpExceptionEventArgs> TcpException;

        /// <summary>
        /// 异常发生时调用
        /// </summary>
        /// <param name="ex"></param>
        protected virtual void OnTcpException(Exception ex)
        {
            if (TcpException == null) return;
            TcpException(this, new TcpExceptionEventArgs(ex));
        }

        /// <summary>
        /// 收到数据 事件
        /// <remarks>
        /// SocketAsyncEventArgs.UserToken中携带有客户端Id，相关信息
        /// </remarks>
        /// </summary>
        public event EventHandler<SocketAsyncEventArgs> DataReceive;

        /// <summary>
        /// 收到数据  时调用
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnDataReceive(SocketAsyncEventArgs e)
        {
            if (DataReceive == null) return;
            DataReceive(this, e);
        }

        #region 消息跟踪事件  OnTrace

        /// <summary>
        /// 调试消息 事件
        /// </summary>
        public event EventHandler<MessageEventArgs> Trace;


        /// <summary>
        /// 调试消息
        /// </summary>
        /// <param name="format">占位格式化字符串</param>
        /// <param name="pms">参数</param>
        protected void OnTrace(string format, params object[] pms)
        {
            if (Trace == null) return;
            Trace(this,new MessageEventArgs(string.Format(format, pms)));
        }


        #endregion

        #endregion

        #region 属性

        /// <summary>
        /// 是否连接
        /// </summary>
        public bool IsConnected { get; set; }
        /// <summary>
        /// 客户端终结点
        /// </summary>
        public IPEndPoint ClientEndPoint
        {
            get { return _clientSocket.LocalEndPoint as IPEndPoint; }
        }

        #endregion

        #region 公有方法

        /// <summary>
        /// 连接服务端
        /// </summary>
        /// <param name="ipv4"></param>
        /// <param name="port"></param>
        public void Connect(string ipv4, int port)
        {
            var iep = new IPEndPoint(IPAddress.Parse(ipv4), port);
            Connect(iep);
        }
        /// <summary>
        /// 连接服务端
        /// </summary>
        /// <param name="serverEndPoint"></param>
        public void Connect(IPEndPoint serverEndPoint)
        {
            if (IsConnected) return;
            _serverEndPoint = serverEndPoint;
            _clientSocket = new Socket(_serverEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

            //采取同步连接方式
            _clientSocket.Connect(_serverEndPoint);
            if (!_clientSocket.Connected) return;

            IsConnected = true;

            Tracer.Debug("[{0}] is connected.",ClientEndPoint);

            //连接成功后准备接受
            Receive();
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        public void Disconnect()
        {
            _clientSocket.Disconnect(false);
            TcpSetting.ReleaseSocket(_clientSocket);
            IsConnected = false;
        }

        //public virtual byte[] GetBuffer()
        //{
        //    return ReceiveBuffer.GetAll();
        //}

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="datagram">不为空的包文</param>
        public void Send(byte[] datagram)
        {
            var writer = _writerPool.Pop();
            if (writer == null)
            {
                writer = new SocketAsyncEventArgs();
                writer.Completed += (s, e) => { Interlocked.Add(ref SentByteSize, datagram.Length); ProcessSend(e); };
            }
            writer.SetBuffer(datagram, 0, datagram.Length);
            try
            {
                var willRaiseEvent = _clientSocket.SendAsync(writer);
                if (!willRaiseEvent) ProcessSend(writer);
            }
            catch (Exception ex)
            {
                writer.Dispose();
                OnTcpException(ex);
            }
        }

        #endregion
        
        #region 私有方法

        /// <summary>
        /// 发送处理
        /// </summary>
        /// <param name="context"></param>
        private void ProcessSend(SocketAsyncEventArgs context)
        {
            try
            {
                Interlocked.Increment(ref SendIO);
                if (context.SocketError == SocketError.Success)
                {
                    _writerPool.Push(context);//回收
                }
                else
                {
                    context.Dispose();
                    OnTcpException(new Exception(string.Format("[TcpClient.ProcessSend][ClientId={0}]SocketError={1}", 0, context.SocketError)));
                }
            }
            catch (Exception ex)
            {
                context.Dispose();
                OnTcpException(ex);
            }
        }

        /// <summary>
        /// 开始接受
        /// </summary>
        private void Receive()
        {
            var reader = _readerPool.Pop();
            if (reader == null)
            {
                reader = new SocketAsyncEventArgs();
                reader.Completed += (s, e) => ProcessReceive(e);
            }
            reader.SetBuffer(new byte[BufferSize], 0, BufferSize);
            try
            {
                var isAsync = _clientSocket.ReceiveAsync(reader);
                if (!isAsync) ProcessReceive(reader);   //同步完成
            }
            catch (Exception ex)
            {
                reader.Dispose();
                OnTcpException(ex);
            }
        }

        /// <summary>
        /// 接受处理
        /// </summary>
        /// <param name="context"></param>
        private void ProcessReceive(SocketAsyncEventArgs context)
        {
            try
            {
                Interlocked.Increment(ref ReceiveIO);
                //检测客户端是否关闭
                if (context.BytesTransferred > 0 && context.SocketError == SocketError.Success)
                {
                    Interlocked.Add(ref ReceivedByteSize, context.BytesTransferred);

                    //ReceiveBuffer.Set(context.Buffer, context.Offset, context.BytesTransferred);
                    OnDataReceive(context);
                    //byte[] buffer = BufferManager.TakeBuffer(e.BytesTransferred);
                    var isAsync = _clientSocket.ReceiveAsync(context);//接受下批数据
                    if (!isAsync) ProcessReceive(context);//同步完成，立即处理
                }
                else
                {
                    //连接断开
                    context.Dispose();
                    OnTcpException(new Exception(string.Format("[TcpClient.ProcessReceive][ClientId={0}]SocketError={1}", 0, context.SocketError)));
                }
            }
            catch (Exception ex)
            {
                context.Dispose();
                OnTcpException(ex);
            }
        }

        #endregion


    }
}